

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2019/11/05 14:42:38
// Design Name: General CPU Cache
// Module Name: Cache
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//      write-back
//      fetch-on-write
//      4ways-set-assoc
//      single core
//      instruction prefetch
//      DWORD aligned(byte align implemented by union indexing)
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
// 参数:
// DATA BITS:32
// ADDRESS SPACE BITS:32
// GROUPS:32
// WAY PER GROUP:4
// CACHELINE SIZE:64Bytes 16Words
// BRAM SIZE:8192Bytes
// BRAM DEPTH:2048Words
`include "axi_type_def.svh"
module cache_onebram(
   input bit addren,
   input bit clk,
   input bit resetn,
   input wire [31:0]PHY_ADDR,
   input bit RD_wr,
   input bit [31:0]DATAin,
   output bit [31:0]DATAout,
   output bit stall, // REG?
   output bit data_ok,
   output bit addr_ok,
   output axi_req_t axi_req,
   input  axi_resp_t axi_resp,
   input bit [3:0]wstrb);
parameter CONST_W_CACHE = 1;
parameter CONST_R_CACHE = 2;
typedef enum logic [2:0] {
   CACHE_S_IDLE      = 3'b001,
   CACHE_S_LRU_R     = 3'b010,
   CACHE_S_RESET     = 3'b100,
   CACHE_S_UNKNOWN   = 3'bx
   } cache_state_t;
      typedef enum logic [4:0] {
   STALL_S_0      = 5'b00001,
   STALL_S_1     = 5'b00010,
   STALL_S_2     = 5'b00100,
   STALL_S_3     = 5'b01000,
   STALL_S_4     = 5'b10000,
   STALL_S_UNKNOWN = 5'bx
   } stall_state_t;
stall_state_t STALL_STATE;
cache_state_t CACHE_STATE;
cache_state_t CACHE_LASTSTATE;
cache_state_t CACHE_NEXTSTATE;
cache_axi_state_t AXI_STATE;
cache_axi_state_t AXI_NEXTSTATE;
bit [31:0]PHY_ADDR_r;
bit RD_wr_r;
bit [31:0]DATAin_r;
bit [3:0]wstrb_r;
bit addren_r;
/*typedef struct packed {
   bit [2:0]R0;
   bit [1:0]R1;
   bit [0:0]R2;
} s_LRU;*/
typedef struct packed {
    bit [7:0] cm_data;
} s_LRU;

typedef struct packed {
   bit valid;
   bit dirty;
   bit [20:0]tag;
} Cache_header;
typedef struct packed {
//   LRU LRU;
   Cache_header [3:0]h;
} Cache_group_header;
s_LRU [31:0]LRU;

Cache_group_header [31:0]Header;
wire[20:0] tag = PHY_ADDR_r[31:11];//15bit
wire[4:0] grpid = PHY_ADDR_r[10:6];//6bit
wire[5:0] offset = PHY_ADDR_r[5:0];//6bit
bit [3:0]cache_block_hit_mask;
bit [1:0]cache_block_hit_ind;
bit READ_TRIGGER;
bit WRITE_TRIGGER;
bit [3:0]obsolete_cacheline_mask;
bit [1 : 0]obsolete_cacheline_ind;
bit [1 : 0]obsolete_cacheline_r;
bit [20:0]obsolete_cacheline_tag;
bit [4:0]obsolete_cacheline_grpid;
bit obsolete_cacheline_valid;
bit obsolete_cacheline_dirty;
bit [10:0]ram_addr_a;
bit [31:0]ram_din_a;
bit [31:0]ram_dout_a;
bit [3:0]ram_wstrb_a;
bit [10:0]ram_addr_b;
bit [31:0]ram_din_b;
bit [31:0]ram_dout_b;
bit [3:0]ram_wstrb_b;
bit [3:0]ram_addr_offset;
bit update_lru;
bit [4:0]lru_group;
bit [3:0]lru_used;
bit [31:0]_DATAout_raw;
assign DATAout = _DATAout_raw;
bit [31:0]PHY_ADDR_l;
bit RD_wr_l;
bit [31:0]DATAin_l;
bit [3:0]wstrb_l;
bit addren_l;
bit[7:0] cm_data;
bit initial_lru;

bit[3:0] valid;
bit[1:0]  update_num;
always_ff @(posedge clk or negedge resetn) begin
   if(!resetn) begin
         PHY_ADDR_l <= 0;
         RD_wr_l <= 1;
         DATAin_l <= 0;
         wstrb_l <= 0;
//         addren_l <= addren;
   end
   else begin
      if(CACHE_STATE == CACHE_S_IDLE) begin
         PHY_ADDR_l <= PHY_ADDR;
         RD_wr_l <= RD_wr;
         DATAin_l <= DATAin;
         wstrb_l <= wstrb;
//         addren_l <= addren;
      end
   end
end
always_comb begin
   if(CACHE_STATE == CACHE_S_IDLE) begin
      PHY_ADDR_r = PHY_ADDR;
      RD_wr_r = RD_wr;
      DATAin_r = DATAin;
      wstrb_r = wstrb;
//      addren_r = addren;
   end
   else begin
      PHY_ADDR_r = PHY_ADDR_l;
      RD_wr_r = RD_wr_l;
      DATAin_r = DATAin_l;
      wstrb_r = wstrb_l;
//      addren_r = addren_l;
   end
end

always_latch begin
   if(!stall) begin
      addren_r = (data_ok || STALL_STATE != STALL_S_0) ? 0:addren;
   end
   else if(!addren)begin
       addren_r = 1'b0;
   end
end
bit stall_s,stall_ss,stall_sss;
always_ff @( posedge clk or negedge resetn) begin
   if(!resetn) begin
      STALL_STATE <= STALL_S_0;
   end
   else begin
      if(data_ok)begin
         STALL_STATE <= STALL_S_4;
      end
      else if(STALL_STATE == STALL_S_4) begin
         STALL_STATE <= STALL_S_3;
      end
      else if (STALL_STATE == STALL_S_3)begin
         STALL_STATE <= STALL_S_2;
      end
      else if (STALL_STATE == STALL_S_2)begin
         STALL_STATE <= STALL_S_1;
      end
      else if (STALL_STATE == STALL_S_1)begin
         STALL_STATE <= STALL_S_0;
      end
   end
end

assign stall = addren_r && !((cache_block_hit_mask!=4'b0) && (CACHE_STATE == CACHE_S_IDLE));
always_comb begin
   cache_block_hit_mask[0] = Header[grpid].h[0].valid && (Header[grpid].h[0].tag == tag);
   cache_block_hit_mask[1] = Header[grpid].h[1].valid && (Header[grpid].h[1].tag == tag);
   cache_block_hit_mask[2] = Header[grpid].h[2].valid && (Header[grpid].h[2].tag == tag);
   cache_block_hit_mask[3] = Header[grpid].h[3].valid && (Header[grpid].h[3].tag == tag);

    valid[0] = Header[grpid].h[0].valid;
    valid[1] = Header[grpid].h[1].valid;
    valid[2] = Header[grpid].h[2].valid;
    valid[3] = Header[grpid].h[3].valid;

   unique case(cache_block_hit_mask)
      4'b1: begin
         cache_block_hit_ind = 0;
      end
      4'b10: begin
         cache_block_hit_ind = 1;
      end
      4'b100: begin
         cache_block_hit_ind = 2;
      end
      4'b1000: begin
         cache_block_hit_ind = 3;
      end
      4'b0: begin
         cache_block_hit_ind = 4'bx;
      end
   endcase
end
blk_mem_gen_1 sram_blk(
   .addra({ram_addr_a}),
   .clka(clk),
   .dina(ram_din_a),
   .douta(ram_dout_a),
   .ena(1'b1),// 1'b1
   .wea(ram_wstrb_a),
   .addrb({ram_addr_b}),
   .clkb(clk),
   .dinb(ram_din_b),
   .doutb(ram_dout_b),//
   //.enb(1),
   .web(ram_wstrb_b)
);//

always_comb begin
   ram_addr_a = {grpid, cache_block_hit_ind, offset[5:2]};
   ram_din_a = DATAin;
   _DATAout_raw = ram_dout_a;
   ram_wstrb_a = (cache_block_hit_mask && addren_r)?wstrb_r:4'b0;
   ram_addr_b = {grpid, obsolete_cacheline_ind, ram_addr_offset};
   ram_din_b = axi_resp.RDATA;
   axi_req.WDATA = ram_dout_b;
   ram_wstrb_b = (AXI_STATE == AXI_R_WAIT_DATA) ? 4'b1111 : 4'b0;
end

/// ######################################################################
always_comb begin
   lru_group = obsolete_cacheline_grpid;
  // !!!!lru_used = (CACHE_STATE==CACHE_S_IDLE) ? cache_block_hit_mask : obsolete_cacheline_mask;
  //更新的录好
   update_num = (CACHE_STATE == CACHE_S_IDLE) ? cache_block_hit_ind : obsolete_cacheline_ind;
  //根据cm_data最后两位的值确定被替换的路号
    //组合逻辑赋值
    unique case (LRU[grpid].cm_data[1:0])
    2'b00:begin
        obsolete_cacheline_mask = 4'b0001;
        obsolete_cacheline_ind = 0;
    end
    2'b01:begin
        obsolete_cacheline_mask = 4'b0010;
        obsolete_cacheline_ind = 1;
    end
    2'b10:begin 
        obsolete_cacheline_mask = 4'b0100;
        obsolete_cacheline_ind = 2;
    end
    2'b11:begin
        obsolete_cacheline_mask = 4'b1000;
        obsolete_cacheline_ind = 3;
    end
    endcase
   obsolete_cacheline_tag = Header[obsolete_cacheline_grpid].h[obsolete_cacheline_ind].tag;
   obsolete_cacheline_grpid = grpid;
   obsolete_cacheline_valid = Header[obsolete_cacheline_grpid].h[obsolete_cacheline_ind].valid;
   obsolete_cacheline_dirty = Header[obsolete_cacheline_grpid].h[obsolete_cacheline_ind].dirty;
end
always_comb begin
end
always_ff @(posedge clk or negedge resetn) begin
   if(!resetn) begin
      LRU <= '0;
   end
   else begin
      cm_data <= LRU[grpid].cm_data;
        if(update_lru) begin
            LRU[grpid].cm_data <= (cm_data[1:0] == update_num) ? {cm_data[1:0], cm_data[7:2]} :
                                  (cm_data[3:2] == update_num) ? {cm_data[3:2], cm_data[7:4], cm_data[1:0]} :
                                  (cm_data[5:4] == update_num) ? {cm_data[5:4], cm_data[7:6], cm_data[3:0]} : cm_data;
         //update_lru <= 0;
        end
        else if(initial_lru)begin
            //initial_lru <= 0;
            LRU[grpid].cm_data <= 8'b11100100;
        end
   end
end
//
/*
CacheExchanger
transfer CACHELINE to memory or transfer data from memory to CACHELINE using AXI protocol
to be done
*/
always_ff @(posedge clk or negedge resetn) begin
   if(!resetn)begin
      CACHE_STATE <= CACHE_S_RESET;
      CACHE_LASTSTATE <= CACHE_S_RESET;
   end
   else begin
      CACHE_STATE <= CACHE_NEXTSTATE;
      CACHE_LASTSTATE <= CACHE_STATE;
      if(!CACHE_STATE) $finish();
   end
end
always_comb begin
   CACHE_NEXTSTATE = CACHE_S_UNKNOWN;
   unique case (CACHE_STATE)
      CACHE_S_RESET:begin
         CACHE_NEXTSTATE = CACHE_S_IDLE;
      end
      CACHE_S_IDLE:begin
         if(addren_r && RD_wr_r && cache_block_hit_mask) begin
            CACHE_NEXTSTATE = CACHE_S_IDLE;
         end
         else if(addren_r && RD_wr_r && !cache_block_hit_mask) begin
            CACHE_NEXTSTATE = CACHE_S_LRU_R;
         end
         else if(addren_r && !RD_wr_r && cache_block_hit_mask) begin
            CACHE_NEXTSTATE = CACHE_S_IDLE;
         end
         else if(addren_r && !RD_wr_r && !cache_block_hit_mask) begin
            CACHE_NEXTSTATE = CACHE_S_LRU_R;
         end
         else begin
            CACHE_NEXTSTATE = CACHE_S_IDLE;
         end
      end
      CACHE_S_LRU_R:begin
         if(AXI_STATE == AXI_IDLE && obsolete_cacheline_valid && obsolete_cacheline_tag==tag) begin
            CACHE_NEXTSTATE = CACHE_S_IDLE;
         end
         else begin
            CACHE_NEXTSTATE = CACHE_S_LRU_R;
         end
      end
   endcase
end
always_comb begin
   update_lru = 1'b0;
   initial_lru = 1'b0;
   unique case (CACHE_STATE)
      CACHE_S_RESET:begin
      end
      CACHE_S_IDLE:begin
         if(addren_r && !RD_wr_r && cache_block_hit_mask) begin
            update_lru = 1;
         end
         else if(addren_r && !RD_wr_r && !cache_block_hit_mask) begin
         end
         else if(addren_r && RD_wr_r && cache_block_hit_mask) begin
            update_lru = 1;
         end
         else if(addren_r && RD_wr_r && !cache_block_hit_mask) begin
         end
         else begin
         end
      end
      CACHE_S_LRU_R:begin
           if(addren_r && !valid)begin
            initial_lru = 1'b1;
            //miss情况下，即使是不能及时更新lru也没关系
            //因为cache的读入需要多个周期
        end
      end
   endcase
end
always_ff @(posedge clk or negedge resetn) begin
   if(!resetn) begin
      obsolete_cacheline_r <= obsolete_cacheline_ind;
      //obsolete_cacheline_tag_r <= obsolete_cacheline_tag;
      WRITE_TRIGGER <= 0;
      READ_TRIGGER <= 0;
      data_ok <= 1'b0;
   end
   else begin
      data_ok <= 1'b0;
      unique case (CACHE_STATE)
         CACHE_S_IDLE:begin
            if(addren_r && CACHE_NEXTSTATE == CACHE_S_IDLE)begin
               data_ok <= 1'b1;
            end
         end
         CACHE_S_RESET:begin
         end
         CACHE_S_LRU_R:begin
            //obsolete_cacheline_r <= obsolete_cacheline_ind;
            if(AXI_STATE == AXI_IDLE && obsolete_cacheline_valid && obsolete_cacheline_dirty && obsolete_cacheline_tag!=tag) begin
               WRITE_TRIGGER <= 1;
            end
            else if(AXI_STATE == AXI_IDLE && !obsolete_cacheline_dirty && (!obsolete_cacheline_valid || obsolete_cacheline_tag!=tag)) begin
               READ_TRIGGER <= 1;
            end
            else if(AXI_STATE == AXI_IDLE && obsolete_cacheline_valid && obsolete_cacheline_tag==tag) begin
               READ_TRIGGER <= 0;
               WRITE_TRIGGER <= 0;
            end
            else begin
               READ_TRIGGER <= 0;
               WRITE_TRIGGER <= 0;
            end
            if(addren_r && CACHE_NEXTSTATE == CACHE_S_IDLE)begin
               data_ok <= 1'b1;
            end
         end
      endcase
   end
end
/*
// TEST
integer cache_trace;
initial begin
   if(i==1) begin
      cache_trace = $fopen("y:/aaa.txt", "w");
   end
   else begin
      cache_trace = $fopen("y:/bbb.txt", "w");
   end
end
*/
// 时序逻辑 状态转移
always_ff @(posedge clk or negedge resetn) begin
   if(!resetn)begin/*(rst_stall != '0) || */
      AXI_STATE <= AXI_IDLE;
   end
   else begin
    
      AXI_STATE <= AXI_NEXTSTATE;
   end
end
always_comb begin
   AXI_NEXTSTATE = AXI_UNKNOWN;
   unique case (AXI_STATE)
      AXI_IDLE:begin
         if(READ_TRIGGER)begin
            AXI_NEXTSTATE = AXI_R_ADDR_SENT;
         end
         else if(WRITE_TRIGGER) begin
            AXI_NEXTSTATE = AXI_W_WAIT_AWREADY;
         end
         else begin
            AXI_NEXTSTATE = AXI_IDLE;
         end
      end
      AXI_R_ADDR_SENT:begin
         if (axi_resp.ARREADY) begin
            AXI_NEXTSTATE = AXI_R_WAIT_DATA;
         end
         else begin
            AXI_NEXTSTATE = AXI_R_ADDR_SENT;
         end
      end
      AXI_R_WAIT_DATA:begin
         if (axi_resp.RVALID && axi_resp.RLAST) begin
            AXI_NEXTSTATE = AXI_IDLE;
         end
         else begin
            AXI_NEXTSTATE = AXI_R_WAIT_DATA;
         end
      end
      AXI_W_WAIT_AWREADY:begin
         if (axi_resp.AWREADY) begin
            AXI_NEXTSTATE = AXI_W_WAIT_DATA;
         end
         else begin
            AXI_NEXTSTATE = AXI_W_WAIT_AWREADY;
         end
      end
      AXI_W_WAIT_DATA:begin
         if (axi_resp.WREADY) begin
            if (ram_addr_offset == 15) begin
               AXI_NEXTSTATE = AXI_W_WAIT_BVALID;
            end
            else begin
               AXI_NEXTSTATE = AXI_W_WAIT_DATA;
            end
         end
         else begin
            AXI_NEXTSTATE = AXI_W_WAIT_DATA;
         end
      end
      AXI_W_WAIT_BVALID:begin
         if (axi_resp.BVALID) begin
            AXI_NEXTSTATE = AXI_IDLE;
         end
         else begin
            AXI_NEXTSTATE = AXI_W_WAIT_BVALID;
         end
      end
   endcase
end
always_ff @(posedge clk or negedge resetn) begin
   if(!resetn) begin
      Header <= '0;
      axi_req.AWVALID <= 1'b0;
      axi_req.ARVALID <= 1'b0;
      axi_req.WVALID <= 1'b0;
   end
   else begin
      axi_req.WID         <= CONST_W_CACHE;
      axi_req.AWID      <= CONST_W_CACHE;
      axi_req.AWADDR      <= {Header[obsolete_cacheline_grpid].h[obsolete_cacheline_ind].tag, obsolete_cacheline_grpid, 6'b0};
      axi_req.AWLEN      <= 4'b1111;//BURST_LEN
      axi_req.AWSIZE      <= 3'b010;
      axi_req.AWBURST   <= 2'b01;
      axi_req.AWLOCK      <= 2'b01;
      axi_req.AWCACHE   <= 4'b0000;
      axi_req.AWPROT      <= 3'b000;
      axi_req.WSTRB      <= 4'b1111;
      axi_req.ARID      <= CONST_R_CACHE;
      axi_req.ARLEN      <= 4'b1111; // BURST_LEN number of data transfers that occur within each burst
      axi_req.ARSIZE      <= 3'b010; // maximum number of data bytes to transfer in each transfer
      axi_req.ARBURST   <= 2'b01; // increnmental
      axi_req.ARLOCK      <= 2'b01;
      axi_req.ARCACHE   <= 4'b0000; // Noncacheable and nonbufferable
      axi_req.ARPROT      <= 3'b000;  // data  access | secure access | normal access
      axi_req.ARADDR      <= {PHY_ADDR_r[31:6], 6'b0};
      axi_req.AWQOS      <= 4'b0000;
      axi_req.ARQOS       <= 4'b0000;
      axi_req.AWREGION    <= 4'b0000;
      axi_req.ARREGION     <= 4'b0000;
      unique case (AXI_STATE)
         AXI_IDLE:begin
            axi_req.AWVALID <= 1'b0;
            axi_req.ARVALID <= 1'b0;
            axi_req.BREADY <= 1'b0;
            axi_req.RREADY <= 1'b0;
            axi_req.WVALID <= 1'b0;//5.27
            axi_req.WLAST <= 1'b0;//5.27
            ram_addr_offset <= 0;
            if(addren_r && RD_wr_r && cache_block_hit_mask) begin
               Header[grpid].h[cache_block_hit_ind].dirty <= 1;
            end
         end
         AXI_R_ADDR_SENT:begin
            if(axi_req.ARVALID && axi_resp.ARREADY) begin
               axi_req.ARVALID <= 1'b0;
            end
            else begin
               axi_req.ARVALID <= 1'b1;
            end
            ram_addr_offset <= 0;
         end
         AXI_R_WAIT_DATA:begin
            axi_req.ARVALID <= 1'b0;
            if (axi_resp.RVALID && axi_resp.RLAST) begin
            end
            else begin
            end
            if (axi_resp.RVALID) begin
               if (axi_resp.RLAST) begin
                  axi_req.RREADY <= 1'b0;// 5.27
                  Header[obsolete_cacheline_grpid].h[obsolete_cacheline_ind].tag <= tag;
                  Header[obsolete_cacheline_grpid].h[obsolete_cacheline_ind].valid <= 1;
                  if(!RD_wr) begin
                  end
               end
               ram_addr_offset <= ram_addr_offset + 1;//5.27
            end
            else begin
               axi_req.RREADY <= 1'b1;
            end
         end
         AXI_W_WAIT_AWREADY:begin
            if (axi_req.AWVALID && axi_resp.AWREADY) begin
               axi_req.AWVALID <= 1'b0;
            end
            else begin
               axi_req.AWVALID <= 1'b1;
               axi_req.WVALID <= 1'b0;
            end
            ram_addr_offset <= 0;
         end
         AXI_W_WAIT_DATA:begin
            axi_req.AWVALID <= 1'b0;
            if (axi_resp.WREADY) begin
               axi_req.WVALID <= 1'b1;
               ram_addr_offset <= ram_addr_offset + 1;//5.27
               if (ram_addr_offset == 15) begin
                  axi_req.WLAST <= 1'b1;
               end
               else begin
                  axi_req.WLAST <= 1'b0;
               end
            end
            else begin
               ram_addr_offset <= 0;
            end
         end
         AXI_W_WAIT_BVALID:begin
            if (axi_req.BREADY && axi_resp.BVALID) begin
               Header[obsolete_cacheline_grpid].h[obsolete_cacheline_ind].dirty <= 0;
               axi_req.BREADY <= 1'b0;
            end
            else begin
               axi_req.BREADY <= 1'b1;
            end
            axi_req.WLAST <= 1'b0;
            axi_req.WVALID <= 1'b0;
         end
      endcase
   end
end
endmodule